package shard

import (
	"errors"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"sync"
)

const (
	MaxShardResourceSize = 32
)

//故障切换
type Failover interface {
	Fail(r *ShardResource) bool
	Recover(r *ShardResource) bool
}

////切片
//type Slice interface {
//	//	Prepare()
//	//
//	Add(shardResource ShardResource)
//	Remove(shardResource ShardResource)
//	AddParams(key string, value string)
//	CopyParams(map[string]string)
//	//
//	Get(seed uint, resourceFeature ResourceFeature) (*ShardResource, error)
//	GetAny(seed uint) (*ShardResource, error)
//}

//片,或者是提供一致服务的资源，比如一组redis，一组wepapps
type Shard struct {
	Name               string            //片n名称
	ShardId            uint              //shard id
	Alias              string            //片别名
	ExtraInfo          map[string]string //扩展信息
	ShardResources     []ShardResource   //片资源
	index              int               //片资源max_index
	plotter            Plotter           //LoadBalance
	availableResources []ShardResource   //可用资源
	availabledIndex    int               //片资源max_index
	failResources      []ShardResource   //失败资源
	failedIndex        int               //片资源max_index
	//
	lock sync.Mutex

	autoSeq uint //序列
}

func NewShard(id uint) *Shard {
	shard := new(Shard)
	shard.ShardId = id
	shard.Name = strconv.Itoa(int(id))
	shard.Init()
	shard.Name = strconv.Itoa(int(id))

	return shard
}

func NewShardByName(name string) *Shard {
	shard := new(Shard).Init()
	shard.lock.Lock()
	defer shard.lock.Unlock()
	shard.autoSeq++
	shard.ShardId = shard.autoSeq
	shard.Name = name
	return shard
}

func NewShardByIdName(id uint, name string) *Shard {
	shard := new(Shard).Init()
	shard.ShardId = id
	shard.Name = name
	return shard
}

func (s *Shard) Init() *Shard {
	s.ShardResources = make([]ShardResource, 0, 3)
	s.availableResources = make([]ShardResource, 0, 3)
	s.failResources = make([]ShardResource, 0, 3)
	s.ExtraInfo = make(map[string]string)
	s.lock = sync.Mutex{}
	if s.Name == "" {
		s.Name = strconv.Itoa(int(s.ShardId))
	}
	return s
}

//匹配片名
func (s *Shard) Match(name string) bool {
	b, err := regexp.MatchString(s.Name, name)
	if err != nil {
		return false
	}
	return b
}

//添加片资源到片
func (s *Shard) Add(reses ...ShardResource) {
	s.lock.Lock()
	defer s.lock.Unlock()
	for _, r := range reses {
		s.ShardResources = append(s.ShardResources, r)
		s.availableResources = append(s.availableResources, r)
	}
}

//添加片资源到片
func (s *Shard) AddMuti(reses []ShardResource) {
	s.lock.Lock()
	defer s.lock.Unlock()
	for _, r := range reses {
		s.ShardResources = append(s.ShardResources, r)
		s.availableResources = append(s.availableResources, r)
	}
}

//移除片资源
func (s *Shard) Remove(r *ShardResource) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.ShardResources, _ = remove(s.ShardResources, r)
	s.availableResources, _ = remove(s.availableResources, r)
}

//从特定片移除片资源
func remove(sres []ShardResource, r *ShardResource) ([]ShardResource, error) {
	if sres == nil || r == nil {
		return sres, errors.New("all of res are null")
	}
	var deletedIndex int = -1
	for index, res := range sres {
		if r.Equals(res) {
			deletedIndex = index
			break
		}
	}
	if deletedIndex < 0 {
		return sres, errors.New(fmt.Sprintf("Not existed resource,can't be removed: %v.", r))
	}
	return append(sres[:deletedIndex], sres[deletedIndex+1:]...), nil

}

//添加扩展参数
func (s *Shard) AddParams(key string, value string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.ExtraInfo[key] = value
	copyToShardResource(s.ShardResources, key, value)
	copyToShardResource(s.availableResources, key, value)
	copyToShardResource(s.failResources, key, value)
}

//添加扩展参数片资源扩展参数
func copyToShardResource(reses []ShardResource, key string, value string) {
	for _, res := range reses {
		res.ExtraInfo[key] = value
	}
}

//添加扩展参数片扩展参数
func (s *Shard) CopyParams(kv map[string]string) {
	for k, v := range kv {
		s.AddParams(k, v)
	}
}

//
func (s *Shard) getShardResources(resourceFeature ResourceFeature) []ShardResource {

	size := len(s.availableResources)
	shardResources := make([]ShardResource, size)
	i := 0
	for _, res := range s.ShardResources {
		if res.Feature == resourceFeature {
			shardResources[i] = res
			i++
		}
	}
	return shardResources[:i]
}

//获取resourceFeature指定的片资源
func (s *Shard) Get(seed uint, resourceFeature ResourceFeature) (*ShardResource, error) {
	plotter := s.plotter
	shardResource, err := plotter.to(seed, s.getShardResources(resourceFeature), s.failResources)
	if err != nil {
		return shardResource, err
	}
	if &shardResource == nil {
		err := errors.New("shard resource not found ")
		return nil, err
	}
	return shardResource, nil
}

//获取任意片资源
func (s *Shard) GetAny(seed uint) (*ShardResource, error) {
	plotter := s.plotter
	shardResource, err := plotter.to(seed, s.availableResources, s.failResources)
	if err != nil {
		return shardResource, err
	}
	if shardResource == nil {
		err := errors.New("shard not found ")
		return nil, err
	}
	return shardResource, nil
}

//失败一个片资源
func (s *Shard) Fail(r *ShardResource) bool {
	s.lock.Lock()
	defer s.lock.Unlock()
	var err error
	s.availableResources, err = remove(s.availableResources, r)
	if err == nil {
		s.failResources = append(s.failResources, *r)
	} else {
		log.Println(err)
		return false
	}
	return true
}

//恢复一个片资源
func (s *Shard) Recover(r *ShardResource) bool {
	s.lock.Lock()
	defer s.lock.Unlock()
	var err error
	s.failResources, err = remove(s.failResources, r)
	if err == nil {
		s.availableResources = append(s.availableResources, *r)
	} else {
		log.Println(err)
		return false
	}
	return true
}
